package com.findmeastore.bc.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;

import com.findmeastore.bc.IBusinessDetailsBC;
import com.findmeastore.bo.BusinessAddress;
import com.findmeastore.bo.BusinessEntity;
import com.findmeastore.bo.CategoryBo;
import com.findmeastore.bo.CityBo;
import com.findmeastore.bo.ContactDetails;
import com.findmeastore.bo.LocalityBo;
import com.findmeastore.bo.BrandBo;
import com.findmeastore.bo.ProductBo;
import com.findmeastore.bo.RelBusinessBrand;
import com.findmeastore.bo.RelBusinessCategory;
import com.findmeastore.bo.RelBusinessProduct;
import com.findmeastore.bo.StateBo;
import com.findmeastore.dao.IBrandDAO;
import com.findmeastore.dao.IBusinessDAO;
import com.findmeastore.dao.ICategoryDAO;
import com.findmeastore.dao.ILocalityDAO;
import com.findmeastore.dao.IProductDAO;
import com.findmeastore.dto.BusinessDetailsDTO;
import com.findmeastore.dto.CategoryDto;
import com.findmeastore.dto.ContactDto;
import com.findmeastore.dto.ProductDto;
import com.findmeastore.dto.RelBusinessCatProdDto;
import com.findmeastore.dto.RelBusinessProdBrandDto;
import com.findmeastore.util.BeanCopyUtil;
import com.findmeastore.util.CollectionUtil;
import com.findmeastore.vo.CityVO;
import com.findmeastore.vo.LocalityVO;

public class BusinessDetailsBcImpl implements IBusinessDetailsBC{

	private IProductDAO productDao;
	private ILocalityDAO localityDao;
	private ICategoryDAO categoryDao;
	private IBusinessDAO businessDao;
	private IBrandDAO brandDao;

	public List<CategoryBo> getAllCategory() {
		return categoryDao.getAllCategories();
	}

	public List<CityVO> getCitiesByStateId(Integer stateId) {
		List<CityVO> cityDataList = new ArrayList<CityVO>();
		List<CityBo> cityList = localityDao.getCityByStateId(stateId);
		for(CityBo cityBo : cityList){
			CityVO cityData = new CityVO();
			cityData.setCityId(cityBo.getId());
			cityData.setCityName(cityBo.getName());
			cityDataList.add(cityData);
		}
		return cityDataList;
	}

	public List<ProductBo> getAllProducts() {
		return productDao.getAllProducts();
	}

	public List<StateBo> getAllStates() {
		return localityDao.getAllStates();
	}

	public void setProductDao(IProductDAO productDao) {
		this.productDao = productDao;
	}

	public List<LocalityVO> getLocalitiesByCityId(Integer cityId) {
		List<LocalityVO> localityDataList = new ArrayList<LocalityVO>();
		List<LocalityBo> localityList = localityDao.getAllLocalitiesByCityId(cityId);
		for(LocalityBo locality : localityList){
			LocalityVO localityData = new LocalityVO();
			localityData.setLocalityId(locality.getId());
			localityData.setLocalityName(locality.getName());
			localityDataList.add(localityData);
		}
		return localityDataList;
	}

	public void saveBusinessProductsRel(List<RelBusinessCatProdDto> businessProductRel, Integer lastModifiedById) {
		Set<RelBusinessProduct> relBusinessProducts = new HashSet<RelBusinessProduct>();
		Integer businessId = null;
		if(CollectionUtils.isNotEmpty(businessProductRel)) {
			for(RelBusinessCatProdDto dto : businessProductRel) {
				businessId = dto.getBusinessId();
				RelBusinessCategory relBusinessCategory = businessDao.getBusinessCatRelById(dto.getBusinessCatRelId());
				if(relBusinessCategory == null || ! relBusinessCategory.getBusinessId().equals(dto.getBusinessId())) {
					//TODO:Srinivas, Need To add Business specific Exception
					throw new RuntimeException("Unable to process: Invalid Category Business mapping");
				}
				Integer[] seletedProducts = dto.getProductIds();
				if(seletedProducts != null && seletedProducts.length > 0) {
					for(Integer productId : seletedProducts) {
						ProductBo productBo = productDao.getProductById(productId);
						if(productBo == null) {
							//TODO:Srinivas, Need To add Business specific Exception
							throw new RuntimeException("Unbale to process: Invalid product selection");
						}
						Set<CategoryBo> categoryBos = productBo.getCategories();
						if(CollectionUtils.isEmpty(categoryBos) || ! categoryBos.contains(relBusinessCategory.getMasterCategory())) {
							//TODO:Srinivas, Need To add Business specific Exception
							throw new RuntimeException("Unable to process: Invalid product category mapping");
						}
					}
				}
					
					Set<RelBusinessProduct> existingRelBusinessProducts = relBusinessCategory.getRelBusinessProducts();
					if(CollectionUtils.isNotEmpty(existingRelBusinessProducts)) {
						Set<RelBusinessProduct> removeBusinessProductRels = new HashSet<RelBusinessProduct>();
						for(RelBusinessProduct relBusinessProduct : existingRelBusinessProducts) {
							if(! CollectionUtil.contains(relBusinessProduct.getProductId(), seletedProducts)) {
								removeBusinessProductRels.add(relBusinessProduct);
							}
						}
						existingRelBusinessProducts.removeAll(removeBusinessProductRels);
					} else if(existingRelBusinessProducts == null) {
						existingRelBusinessProducts = new HashSet<RelBusinessProduct>();
						relBusinessCategory.setRelBusinessProducts(existingRelBusinessProducts);
					}
					
					if(seletedProducts != null && seletedProducts.length > 0) {
						
						for(Integer productId : seletedProducts) {
							ProductBo productBo = productDao.getProductById(productId);
							RelBusinessProduct existingRelBusinessProduct = null;
							for(RelBusinessProduct existingRel : existingRelBusinessProducts) {
								if(existingRel.getProductId().equals(productId)) {
									existingRelBusinessProduct = existingRel;
								}
							}
							
							if(existingRelBusinessProduct == null) {
								existingRelBusinessProduct = new RelBusinessProduct();
								existingRelBusinessProduct.setProductId(productId);
								existingRelBusinessProduct.setBusinessId(dto.getBusinessId());
								existingRelBusinessProduct.setCategoryId(dto.getCategoryId());
								existingRelBusinessProduct.setBusinessCategoryRelId(dto.getBusinessCatRelId());
							}
							existingRelBusinessProducts.add(existingRelBusinessProduct);
						}
					} 
			}
			
		}
		if(businessId != null) {
			BusinessEntity businessEntity = businessDao.getBusinessEntity(businessId);
			businessEntity.setLastModifiedByUserId(lastModifiedById);
			businessDao.saveBusiness(businessEntity);			
		}
	}
	
	public BusinessEntity getBusinessById(Integer id) {
		return businessDao.getBusinessEntity(id);
	}

	public IProductDAO getProductDao() {
		return productDao;
	}

	public void setLocalityDao(ILocalityDAO localityDao) {
		this.localityDao = localityDao;
	}

	public ILocalityDAO getLocalityDao() {
		return localityDao;
	}

	public void setCategoryDao(ICategoryDAO categoryDao) {
		this.categoryDao = categoryDao;
	}

	public ICategoryDAO getCategoryDao() {
		return categoryDao;
	}

	public IBusinessDAO getBusinessDao() {
		return businessDao;
	}

	public void setBusinessDao(IBusinessDAO businessDao) {
		this.businessDao = businessDao;
	}

	public IBrandDAO getBrandDao() {
		return brandDao;
	}

	public void setBrandDao(IBrandDAO brandDao) {
		this.brandDao = brandDao;
	}

	public Integer saveBusinessEntity(BusinessDetailsDTO businessDetailsDTO) {
		Integer businessId = businessDetailsDTO.getBusinessId();
		boolean isUpdate = (businessId != null && businessId > 0) ? true : false;
		
		BusinessEntity businessEntity = null;
		if(isUpdate) {
			businessEntity = businessDao.getBusinessEntity(businessId);
			if(businessEntity == null) {
				throw new RuntimeException("Unable to process");
			}
		} else {
			businessEntity = new BusinessEntity();
			businessEntity.setCreationTstamp(new Date());
		businessEntity.setCreatedByUserId(businessDetailsDTO.getModifiedById());
		}
		businessEntity.setLastModifiedTstamp(new Date());
		businessEntity.setLastModifiedByUserId(businessDetailsDTO.getModifiedById());
		businessEntity.setName(businessDetailsDTO.getBusinessName());
		businessEntity = businessDao.saveBusiness(businessEntity);
		businessId = businessEntity.getId();
		if(businessId != null && businessId > 0){
			BusinessAddress businessAddress = new BusinessAddress();
			businessAddress.setBusinessId(businessId);
			businessAddress.setCityId(new Integer(businessDetailsDTO.getCity()));
			businessAddress.setStateId(new Integer(businessDetailsDTO.getState()));
			businessAddress.setStreet1(businessDetailsDTO.getAddress1());
			businessAddress.setStreet2(businessDetailsDTO.getAddress2());
			businessAddress.setStreet3(businessDetailsDTO.getAddress3());
			businessAddress.setPinCode(new Integer(businessDetailsDTO.getPinCode()));
			businessAddress.setCreationTstamp(new Date());
			businessAddress.setCreatedByUserId(businessDetailsDTO.getModifiedById());
			businessAddress.setLastModifiedTstamp(new Date());
			businessAddress.setLastModifiedByUserId(businessDetailsDTO.getModifiedById());
			businessAddress.setLocalityId(businessDetailsDTO.getLocalityId());
			businessDao.saveBusinessAddress(businessAddress);
			updateContactDetails(businessDetailsDTO.getContactList(), businessId, businessEntity.getLastModifiedByUserId());
			updateBusinessCategories(businessDetailsDTO.getSelectedCategory(), businessEntity);
		}
		return businessId;
	}

	private void updateBusinessCategories(Integer[] selectedCategories, BusinessEntity businessEntity) {
		
	    Set<RelBusinessCategory> relBusinessCategories = businessEntity.getRelBusinessCategories();

	    if (CollectionUtils.isNotEmpty(relBusinessCategories)) {
	      Set<RelBusinessCategory> toRemoveCategories = new HashSet<RelBusinessCategory>();

	      for (RelBusinessCategory relBo : relBusinessCategories) {
	        if (!CollectionUtil.contains(relBo.getCategoryId(), selectedCategories)) {
	          toRemoveCategories.add(relBo);
	        }
	      }

	      relBusinessCategories.removeAll(toRemoveCategories);

	    } else if (relBusinessCategories == null) {
	    	
	    	relBusinessCategories = new HashSet<RelBusinessCategory>();
	        businessEntity.setRelBusinessCategories(relBusinessCategories);
	    }

	    if (selectedCategories != null && selectedCategories.length > 0) {
	      for (int i = 0; i < selectedCategories.length; i++) {

	        CategoryBo categoryBo = categoryDao.getCategoryById(selectedCategories[i]);

	        if (categoryBo == null) {// For non existing Category
	          throw new RuntimeException("Invalid category");
	        }
	        RelBusinessCategory existingRel = null;
	        for(RelBusinessCategory existingRelBusinessCategory : relBusinessCategories) {
	        	if(existingRelBusinessCategory.getCategoryId().equals(selectedCategories[i])) {
	        		existingRel = existingRelBusinessCategory;
	        	}
	        }
	        if (existingRel == null) {
	        	RelBusinessCategory newRelBo = new RelBusinessCategory();
	          newRelBo.setCategoryId(categoryBo.getId());
	          newRelBo.setBusinessId(businessEntity.getId());
	          relBusinessCategories.add(newRelBo);
	        }

	      }
	    }
	}
	
	private void updateContactDetails(List<ContactDto> contactList, Integer businessId, Integer userId) {
		if(CollectionUtils.isNotEmpty(contactList)){
			for(ContactDto contactDto : contactList){
				ContactDetails contactDetails = null;
				Integer id = contactDto.getId();
				boolean isUpdate = (id != null && id > 0)? true : false;
				if(isUpdate) {
					contactDetails = businessDao.getBusinessContactById(id);
					if(contactDetails == null) {
						throw new RuntimeException("Unable to process");
					}
					
				} else {
					contactDetails = new ContactDetails();
					contactDetails.setCreatedByUserId(userId);
					contactDetails.setCreationTstamp(new Date());
				}
				BeanCopyUtil.copy(contactDetails, contactDto);
				contactDetails.setBusinessId(businessId);
				contactDetails.setLastModifiedTstamp(new Date());
				contactDetails.setLastModifiedByUserId(userId);
				contactDetails.setConDetails(contactDto.getContactDetails());
				businessDao.saveBusinessContact(contactDetails);
			}
		}
	}


	public List<BusinessEntity> getAllBusiness() {
		return businessDao.getAllBusiness();
	}


	public List<CategoryDto> getSelectedCategories(Integer businessId) {
		List<CategoryDto> selectedCategoriesList = new ArrayList<CategoryDto>();
		if(businessId!=null && businessId > 0){
			BusinessEntity businesEntity = businessDao.getBusinessEntity(businessId);
			List<CategoryBo> categoryBoList = new ArrayList<CategoryBo>(businesEntity.getCategory());
			for(CategoryBo categoryBo : categoryBoList){
				CategoryDto categoryDto = new CategoryDto();
				List<ProductDto> productsList = new ArrayList<ProductDto>();
				categoryDto.setId(categoryBo.getId());
				categoryDto.setName(categoryBo.getName());
				for(ProductBo productBo : categoryBo.getProducts()){
					ProductDto productDto = new ProductDto();
					productDto.setId(productBo.getId());
					productDto.setName(productBo.getName());
					productsList.add(productDto);
				}
				categoryDto.setProducts(productsList);
				selectedCategoriesList.add(categoryDto);
			}			
		}
		return selectedCategoriesList;
	}

	public void saveBusinessBrandsRel(List<RelBusinessProdBrandDto> businessBrandRel,Integer lastModifiedById) {
		Set<RelBusinessBrand> relBusinessBrands = new HashSet<RelBusinessBrand>();
		Integer businessId = null;
		if(CollectionUtils.isNotEmpty(businessBrandRel)) {
			for(RelBusinessProdBrandDto dto : businessBrandRel) {
				businessId = dto.getBusinessId();
				RelBusinessProduct relBusinessProduct = businessDao.getBusinessProductRelById(dto.getBusinessProdRelId());
				if(relBusinessProduct == null || ! relBusinessProduct.getBusinessId().equals(dto.getBusinessId())) {
					//TODO:Srinivas, Need To add Business specific Exception
					throw new RuntimeException("Unable to process: Invalid Category Business mapping");
				}
				Integer[] seletedBrands = dto.getBrandIds();
				if(seletedBrands != null && seletedBrands.length > 0) {
					for(Integer brandId : seletedBrands) {
						BrandBo brandBo = brandDao.getBrandById(brandId);
						if(brandBo == null) {
							//TODO:Srinivas, Need To add Business specific Exception
							throw new RuntimeException("Unbale to process: Invalid brand selection");
						}
						Set<ProductBo> productBos = brandBo.getProducts();
						if(CollectionUtils.isEmpty(productBos) || ! productBos.contains(relBusinessProduct.getMasterProduct())) {
							//TODO:Srinivas, Need To add Business specific Exception
							throw new RuntimeException("Unable to process: Invalid product category mapping");
						}
					}
				}
					
					Set<RelBusinessBrand> existingRelBusinessBrands = relBusinessProduct.getRelBusinessBrands();
					if(CollectionUtils.isNotEmpty(existingRelBusinessBrands)) {
						Set<RelBusinessBrand> removeBusinessBrandRels = new HashSet<RelBusinessBrand>();
						for(RelBusinessBrand relBusinessBrand : existingRelBusinessBrands) {
							if(! CollectionUtil.contains(relBusinessBrand.getProductId(), seletedBrands)) {
								removeBusinessBrandRels.add(relBusinessBrand);
							}
						}
						existingRelBusinessBrands.removeAll(removeBusinessBrandRels);
					} else if(existingRelBusinessBrands == null) {
						existingRelBusinessBrands = new HashSet<RelBusinessBrand>();
						relBusinessProduct.setRelBusinessBrands(existingRelBusinessBrands);
					}
					
					if(seletedBrands != null && seletedBrands.length > 0) {
						
						for(Integer brandId : seletedBrands) {
							BrandBo brandBo = brandDao.getBrandById(brandId);
							RelBusinessBrand existingRelBusinessBrand = null;
							for(RelBusinessBrand existingRel : existingRelBusinessBrands) {
								if(existingRel.getProductId().equals(brandId)) {
									existingRelBusinessBrand = existingRel;
								}
							}
							
							if(existingRelBusinessBrand == null) {
								existingRelBusinessBrand = new RelBusinessBrand();
								existingRelBusinessBrand.setBrandId(brandId);
								existingRelBusinessBrand.setProductId(dto.getProductId());
								existingRelBusinessBrand.setBusinessId(dto.getBusinessId());
								existingRelBusinessBrand.setCategoryId(dto.getCategoryId());
								existingRelBusinessBrand.setBusinessProductRelId(dto.getBusinessProdRelId());
							}
							existingRelBusinessBrands.add(existingRelBusinessBrand);
						}
					} 
			}
			
		}
		if(businessId != null) {
			BusinessEntity businessEntity = businessDao.getBusinessEntity(businessId);
			businessEntity.setLastModifiedByUserId(lastModifiedById);
			businessDao.saveBusiness(businessEntity);			
		}
		
	}
}
